/**
 * @ProjectName: 
 * @Copyright: 2014 lisheng  All Right Reserved.
 * @address: toughheart@163.com
 * @date: 2015年10月12日 下午8:20:17
 * @Description: 本内容未经本人允许禁止使用、转发.
 */

package com.ls.fw.web.file.controller;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.support.DefaultMultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import com.ls.fw.commons.core.bean.RestStatus;
import com.ls.fw.commons.core.bean.ResultObj;
import com.ls.fw.commons.core.unit.ByteSizeValue;
import com.ls.fw.commons.core.unit.SizeValue;
import com.ls.fw.commons.core.utils.file.FileUtil;
import com.ls.fw.commons.spring.SpringContextHolder;
import com.ls.fw.web.file.bean.DownInfo;
import com.ls.fw.web.file.bean.FileErrorInfo;
import com.ls.fw.web.file.bean.FileLimit;
import com.ls.fw.web.file.bean.UploadFileInfo;
import com.ls.fw.web.file.constant.Global;
import com.ls.fw.web.file.service.FileService;

/**
 * 文件控制器 初始化参数： 根目录："baseDir"; 是否为绝对路径："isAbsolutePath"; 文件临时路径："tempdir";
 * 单个最大文件大小："maxFileSize"; 最大内存大小："maxMemSize"; 允许上传的类型（多个用“,”分割）："allowType";
 * 不允许上传的类型（多个用“,”分割）："denyType";
 * 
 * @author lisheng
 * @date 2015年10月12日 下午8:20:17
 * @version V1.0
 */
@Controller
@RequestMapping(value = "/file")
public class FileController extends BaseFileController implements InitializingBean,
		ServletContextAware {

	public static final long BASE_SIZE = 1024;

	// 1GB
	public static final long MAX_SIZE = BASE_SIZE * BASE_SIZE * BASE_SIZE * 1;

	/** 保存文件名称 */
	public static final String SAVENAME = "saveName";

	/** 保存文件相对目录 */
	public static final String RELATIVEDIR = "relativeDir";
	
	public static final String PATH = "path";

	protected boolean isAbsolutePath = false;

	protected FileLimit fileLimit = new FileLimit();

	protected UploadHandler hander = new MyUploadHandler();

	@Autowired
	protected FileService defaultFileService;

	private UploadReturnResultHandler returnHander = null;
	
	private DownReturnResultHandler downReturnHander = null;
	
	/**
	 * 是否加密
	 */
	private boolean encrypt = false;
	/**
	 * 下载文件
	 */
	@RequestMapping(value = "/" + DOWNLOAD+"/{path}/do")
	public Object download1(HttpServletRequest request
			, @PathVariable(value="path") String path
			, String saveName)
			throws IOException {
		//TODO:是否加密
		DownInfo downInfo  = new DownInfo();
		downInfo.setPath(path);
		downInfo.setSaveName(saveName);
		try {
			if(!check(downInfo)){
				if(this.downReturnHander != null){
					return downReturnHander.paraInValid(downInfo,request,null);
				}
			}
		} catch (Exception e) {
			if(this.downReturnHander != null){
				return downReturnHander.paraInValid(downInfo,request,e);
			}
		}
		saveName = downInfo.getSaveName();
		String f = fileLimit.getBaseDir()+File.separator+path;
		File f1 = new File(f);
		if(!f1.exists()){
			this.logger.error("文件"+f1.getPath()+"不存在！");
			if(this.downReturnHander != null){
				return downReturnHander.noExists(downInfo,request,null);
			}
		}
		if(this.downReturnHander != null){
			return this.downReturnHander.returnHandler(downInfo,f1, request);
		}
		return super.download(request, saveName, f1);
	}

	
	/**
	 * 
	 * @author lisheng
	 * @date 2015年10月18日 下午9:18:50
	 * @version V1.0
	 * @param dir
	 * @param name
	 * @param saveName2
	 * @return
	 */
	private boolean check(DownInfo downInfo) {
		String dir = downInfo.getPath();
		String saveName = downInfo.getSaveName();
		if(StringUtils.isBlank(dir)){
			throw new IllegalArgumentException("请求下载的文件路径非法！");
		}
		if(dir.startsWith("../")) {
			throw new IllegalArgumentException("请求下载的文件路径非法！");
		}
		if (dir.startsWith("..\\")) {
			throw new IllegalArgumentException("请求下载的文件路径非法！");
		}
		if(saveName.startsWith("../")) {
			throw new IllegalArgumentException("请求下载的文件名称非法！");
		}
		if (saveName.startsWith("..\\")) {
			throw new IllegalArgumentException("请求下载的文件名称非法！");
		}
		return true;
	}

	/**
	 * 处理上传文件
	 * 
	 * @author lisheng
	 * @date 2015年10月16日 下午9:25:25
	 * @version V1.0
	 */
	class MyUploadHandler implements UploadHandler {

		@Override
		public boolean isValid(String name, MultipartFile file,
				HttpServletRequest request, UploadResultHandler resultHandler)
				throws Exception {
			boolean valid = true;
			String fileName = file.getOriginalFilename();
//			GetTypeByHead.getFileType(file.);
			String ext = FileUtil.getFileExtension(fileName, false);
			if (!isValidExt(ext)) {
				FileErrorInfo info = new FileErrorInfo();
				info.setFileName(fileName);
				info.setMsg("不允许上传以【" + ext + "】为后缀的文件！");
				resultHandler.fail(fileName, file, info, null);
				valid = false;
			}
			if (valid && fileLimit.getMaxFileSize() > 0) {
				long size = file.getSize();
				if (size > fileLimit.getMaxFileSize()) {
					FileErrorInfo info = new FileErrorInfo();
					info.setFileName(fileName);
					ByteSizeValue bsv = new ByteSizeValue(
							fileLimit.getMaxFileSize());
					info.setMsg("不允许上传超过" + bsv.toString() + "的文件！");
					resultHandler.fail(fileName, file, info, null);
					valid = false;
				}
			}
			return valid;
		}

		@Override
		public boolean save(String name, MultipartFile file,
				HttpServletRequest request, UploadResultHandler resultHandler)
				throws Exception {
			boolean result = false;
			String fileName = file.getOriginalFilename();
			String baseRelativeDir = getSaveRelativeDir(request);
			String path = fileLimit.getBaseDir() + baseRelativeDir;
			String saveName = getSaveName(request);
			if (StringUtils.isBlank(saveName)) {
				saveName = file.getOriginalFilename();
			}
			String newFileName = FileUtil.getFinalFileName(path, saveName);
			boolean flag = false;
			if (!newFileName.equals(saveName)) {
				flag = true;
			}
			//TODO:是否加密
			String url = "file/download/"+baseRelativeDir+"/"+newFileName+"?"+SAVENAME+"="+newFileName;
			path = mkDirIfNotExist(path);
			File destfile = new File(path, newFileName);
			try {
				file.transferTo(destfile);
				destfile.setExecutable(false);
				UploadFileInfo fileInfo = creatUploadFileInfo(file,
						baseRelativeDir, newFileName, flag, url);
				resultHandler.success(name, destfile, fileInfo);
				result = true;
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
				FileErrorInfo info = new FileErrorInfo();
				info.setFileName(fileName);
				info.setMsg(e.getMessage());
				resultHandler.fail(name, file, info, e);
			}
			return result;
		}

	}

	@RequestMapping("/" + UPLOAD)
	public ModelAndView upload(DefaultMultipartHttpServletRequest request,
			HttpServletResponse response, WebRequest webRequest)
			throws Exception {
		return this.doUpload(request, webRequest, new UploadWorkHandler() {

			@Override
			public ResultObj doWork(DefaultMultipartHttpServletRequest request,
					WebRequest webRequest) throws Exception {
				final ResultObj obj = ResultObj.newInstance();
				try {
					Map<String, Object> map = new HashMap<String, Object>();
					final List<FileErrorInfo> fail = new ArrayList<FileErrorInfo>();
					final List<UploadFileInfo> success = new ArrayList<UploadFileInfo>();
					final UploadResultHandler resultHandler = new UploadResultHandler() {

						@Override
						public void success(String name, File destfile,
								UploadFileInfo fileInfo) throws Exception {
							success.add(fileInfo);
							defaultFileService.success(name, destfile, fileInfo);
						}

						@Override
						public void fail(String name, MultipartFile file,
								FileErrorInfo info, Exception e)
								throws Exception {
							fail.add(info);
							defaultFileService.fail(name, file, info, e);
						}
					};
					uploadHandler(request, new FileUploadHandler() {

						@Override
						public boolean save(String name, MultipartFile file,
								HttpServletRequest request) throws Exception {
							boolean result = false;
							if (hander.isValid(name, file, request,
									resultHandler)) {
								result = hander.save(name, file, request,
										resultHandler);
							}
							return result;
						}
					});
					map.put("fail", fail);
					map.put("success", success);
					obj.setData(map);
					obj.setStatus(RestStatus.OK);
				} catch (Exception e) {
					logger.error(e.getMessage(), e);
					obj.setMsg(e.getMessage());
				}
				return obj;
			}
		}, returnHander);
	}

	/**
	 * 检查目录是否存在
	 * 
	 * @author lisheng
	 * @date 2015年10月16日 下午9:33:04
	 * @version V1.0
	 * @param dirPath
	 * @return
	 */
	private String mkDirIfNotExist(final String dirPath) {
		String dir = dirPath;
		try {
			dir = this.toLocalSeparator(dir);
			File file = new File(dir);
			if (!file.exists()) {
				file.mkdirs();
			}
		} catch (Exception e) {
			this.logger.info(e.getMessage() + ",请设置下载文件目录！", e);
		}
		return dir;
	}

	/**
	 * 转换为本地分隔符
	 * 
	 * @author lisheng
	 * @date 2015年10月16日 下午9:33:26
	 * @version V1.0
	 * @param dirPath
	 * @return
	 */
	private String toLocalSeparator(final String dirPath) {
		String dir = dirPath;
		try {
			dir = StringUtils.replaceChars(dir, "\\", File.separator);
			dir = StringUtils.replaceChars(dir, "/", File.separator);
		} catch (Exception e) {
			this.logger.info(e.getMessage() + ",请设置下载文件目录！", e);
		}
		return dir;
	}

	private String getSaveName(final HttpServletRequest request) {
		return (String) request.getParameter(SAVENAME);
	}

	/**
	 * 保存相对目录
	 * 
	 * @author Defender 2014年6月19日下午9:46:13
	 * @param request
	 * @return
	 */
	private String getSaveRelativeDir(HttpServletRequest request) {
		String dir = request.getParameter(RELATIVEDIR);
		if (!StringUtils.isBlank(dir)) {
			if (!dir.startsWith(File.separator)) {
				dir = File.separator + dir;
			}
			if (!dir.endsWith(File.separator)) {
				dir = dir + File.separator;
			}
		}
		return dir;
	}

	/**
	 * 创建上传的文件实体
	 * 
	 * @author Defender 2014年2月22日 上午12:39:03
	 * @param item
	 * @param saveDir
	 * @param saveName
	 * @param existCommonName
	 * @param url
	 * @return
	 */
	private UploadFileInfo creatUploadFileInfo(final MultipartFile file,
			String saveRelativeDir, String saveName, boolean existCommonName,
			String url) {
		UploadFileInfo fileInfo = new UploadFileInfo();
		fileInfo.setContentType(file.getContentType());
		fileInfo.setSize(file.getSize());
		ByteSizeValue bsv = new ByteSizeValue(file.getSize());
		fileInfo.setSizeStr(bsv.toString());
		String fileName = file.getOriginalFilename();
		fileInfo.setSrcName(fileName);
		fileInfo.setFileType(FileUtil.getFileExtension(fileName, false));
		fileInfo.setSaveRelativeDir(saveRelativeDir);
		fileInfo.setSaveName(saveName);
		fileInfo.setExistCommonName(existCommonName);
		fileInfo.setUrl(url);
		return fileInfo;
	}

	/**
	 * 是否是允许的文件后缀
	 * 
	 * @author Defender 2014年2月21日 下午10:33:51
	 * @param ext
	 * @return
	 */
	private boolean isValidExt(final String ext) {
		boolean flag = false;
		String[] type = this.fileLimit.getDenyType();
		boolean deny = false;
		boolean allow = true;
		if (type != null) {
			deny = this.match(type, ext);
		}
		if (!deny) {
			type = null;
			type = this.fileLimit.getAllowType();
			if (type != null) {
				allow = this.match(type, ext);
			}
		}
		if (allow && !deny) {
			flag = true;
		}
		return flag;
	}

	/**
	 * 上传文件类型是否匹配要求的
	 * 
	 * @author Defender 2014年2月21日 下午10:17:31
	 * @param type
	 * @param extension
	 * @return
	 */
	private boolean match(final String[] type, final String extension) {
		boolean flag = false;
		String str = ("*." + extension).toUpperCase();
		if (type != null) {
			for (String string : type) {
				if (!string.startsWith("*.")) {
					string = "*." + string;
				}
				if (str.equalsIgnoreCase(string.toUpperCase())) {
					flag = true;
					break;
				}
			}
		}
		return flag;
	}
	
	/**
	 * A 初始化文件限制对象
	 * 
	 * @author Defender 2014年2月21日 下午8:26:49
	 */
	private void intFileLimit(ServletContext servletContext) {
		String rootDir = servletContext.getRealPath("/");
		fileLimit.setRootDir(rootDir);
//		String baseDir = servletContext.getInitParameter(Global.BASEDIR);
//		if (!StringUtils.isBlank(baseDir)) {
//			fileLimit.setBaseDir(baseDir);
//		}
//		String tempdir = servletContext.getInitParameter(Global.tempdir);
//		if (!StringUtils.isBlank(tempdir)) {
//			fileLimit.setTempdir(tempdir);
//		}
//		String maxFileSize = servletContext
//				.getInitParameter(Global.maxFileSize);
//		if (!StringUtils.isBlank(maxFileSize)) {
//			try {
//				long size = SizeValue.parseSizeValue(maxFileSize).singles();
//				if (size > MAX_SIZE) {
//					size = MAX_SIZE;
//				}
//				fileLimit.setMaxFileSize(size);
//			} catch (Exception e) {
//				this.logger.info(e.getMessage(), e);
//			}
//		}
//		String allowType = servletContext
//				.getInitParameter(Global.allowType);
//		if (!StringUtils.isBlank(allowType)) {
//			fileLimit.setAllowType(this.getType(allowType));
//		}
//		
//		String denyType = servletContext.getInitParameter(Global.denyType);
//		if (!StringUtils.isBlank(denyType)) {
//			fileLimit.setDenyType(this.getType(denyType));
//		}
//		// 私定
//		String _isAbsolutePath = servletContext
//				.getInitParameter(BaseDown.ISABSOLUTEPATH);
//		baseDir = this.handlPara(servletContext, baseDir, _isAbsolutePath);
//		fileLimit.setBaseDir(baseDir);
//		fileLimit.setAbsoluteDir(this.isAbsolutePath);
//
//		this.logger.info(this.fileLimit.toString());
	}
	/**
	 * A 初始化文件限制对象
	 * 
	 * @author Defender 2014年2月21日 下午8:26:49
	 */
	private void intFileLimit(Map<String,String> map) {
		if(map==null){
			return;
		}
		String baseDir = map.get(Global.baseDir);
		if (!StringUtils.isBlank(baseDir)) {
			fileLimit.setBaseDir(baseDir);
		}
		String tempdir = map.get(Global.tempdir);
		if (!StringUtils.isBlank(tempdir)) {
			fileLimit.setTempdir(tempdir);
		}
		String maxFileSize = map.get(Global.maxFileSize);
		if (!StringUtils.isBlank(maxFileSize)) {
			try {
				long size = SizeValue.parseSizeValue(maxFileSize).singles();
				if (size > MAX_SIZE) {
					size = MAX_SIZE;
				}
				fileLimit.setMaxFileSize(size);
			} catch (Exception e) {
				this.logger.info(e.getMessage(), e);
			}
		}
		String allowType = map.get(Global.allowType);
		if (!StringUtils.isBlank(allowType)) {
			fileLimit.setAllowType(this.getType(allowType));
		}
		
		String denyType = map.get(Global.denyType);
		if (!StringUtils.isBlank(denyType)) {
			fileLimit.setDenyType(this.getType(denyType));
		}
		// 私定
		String _isAbsolutePath = map.get(Global.isAbsolutePath);
		baseDir = this.handlPara(map, baseDir, _isAbsolutePath);
		fileLimit.setBaseDir(baseDir);
		fileLimit.setAbsoluteDir(this.isAbsolutePath);

		this.logger.info(this.fileLimit.toString());
	}
	/**
	 * 获取文件类型
	 * 
	 * @author lisheng
	 * @date 2015年10月16日 下午9:36:18
	 * @version V1.0
	 * @param type
	 * @return
	 */
	private String[] getType(final String type) {
		String[] strs = null;
		if (!StringUtils.isBlank(type)) {
			strs = type.split(",");
		}
		return strs;
	}

	/**
	 * 处理参数
	 * 
	 * @author lisheng
	 * @date 2015年10月16日 下午9:36:40
	 * @version V1.0
	 * @param servletContext
	 * @param _baseDir
	 * @param _isAbsolutePath
	 * @return
	 */
	protected String handlPara(ServletContext servletContext, String _baseDir,
			String _isAbsolutePath) {
		String baseDir = _baseDir;
		if (!StringUtils.isBlank(_isAbsolutePath)) {
			try {
				if ("true".equalsIgnoreCase(_isAbsolutePath)) {
					this.isAbsolutePath = true;
				}
			} catch (Exception e) {
				this.logger.info(e.getMessage(), e);
			}
		}
		if (StringUtils.isBlank(baseDir)) {
			return "";
		}
		if (!this.isAbsolutePath && !StringUtils.isBlank(baseDir)) {
			// 设置文件上传路径
			String dir = baseDir;
			int index = 0;
			while (dir.startsWith("/")) {
				dir = dir.replaceFirst("/", "");
			}
			while (dir.startsWith("\\")) {
				dir = dir.replaceFirst("\\\\", "");
			}
			while (dir.startsWith("../")) {
				dir = dir.replaceFirst("../", "");
				index++;
			}
			while (dir.startsWith("..\\")) {
				dir = dir.replaceFirst("..\\\\", "");
				index++;
			}
			String upload = "";
			if (index > 0) {
				// this.isAbsolutePath = true ;
				upload = servletContext.getRealPath("/");
				while (index > 0) {
					File f = new File(upload);
					upload = f.getParent();
					index--;
				}
				upload = upload + File.separator + dir;
			} else {
				upload = servletContext.getRealPath(File.separator + dir);
			}
			baseDir = upload;
		}
		if (StringUtils.isBlank(baseDir)) {
			baseDir = servletContext.getRealPath("/");
		}
		this.logger.info("载文件目录:" + baseDir);
		this.logger.info("是否为绝对路径:" + isAbsolutePath);
		return mkDirIfNotExist(baseDir);
	}
	/**
	 * 处理参数
	 * 
	 * @author lisheng
	 * @date 2015年10月16日 下午9:36:40
	 * @version V1.0
	 * @param servletContext
	 * @param _baseDir
	 * @param _isAbsolutePath
	 * @return
	 */
	private String handlPara(Map<String,String> servletContext, String _baseDir,
			String _isAbsolutePath) {
		String baseDir = _baseDir;
		if (!StringUtils.isBlank(_isAbsolutePath)) {
			try {
				if ("true".equalsIgnoreCase(_isAbsolutePath)) {
					this.isAbsolutePath = true;
				}
			} catch (Exception e) {
				this.logger.info(e.getMessage(), e);
			}
		}
		if (StringUtils.isBlank(baseDir)) {
			return "";
		}
		if (!this.isAbsolutePath && !StringUtils.isBlank(baseDir)) {
			// 设置文件上传路径
			String dir = baseDir;
			int index = 0;
			while (dir.startsWith("/")) {
				dir = dir.replaceFirst("/", "");
			}
			while (dir.startsWith("\\")) {
				dir = dir.replaceFirst("\\\\", "");
			}
			while (dir.startsWith("../")) {
				dir = dir.replaceFirst("../", "");
				index++;
			}
			while (dir.startsWith("..\\")) {
				dir = dir.replaceFirst("..\\\\", "");
				index++;
			}
			String upload = "";
			if (index > 0) {
				// this.isAbsolutePath = true ;
				upload = this.fileLimit.getRootDir();
				while (index > 0) {
					File f = new File(upload);
					upload = f.getParent();
					index--;
				}
				upload = upload + File.separator + dir;
			} else {
				upload = this.fileLimit.getRootDir()+File.separator + dir;
			}
			baseDir = upload;
		}
		if (StringUtils.isBlank(baseDir)) {
			baseDir = this.fileLimit.getRootDir();
		}
		this.logger.info("载文件目录:" + baseDir);
		this.logger.info("是否为绝对路径:" + isAbsolutePath);
		return mkDirIfNotExist(baseDir);
	}
	
	
	@Override
	public void setServletContext(ServletContext servletContext) {
		intFileLimit(servletContext);
		LinkedHashMap<String, String> map = Global.configInfo;
		if(map != null){
			String encryptStr = map.get(Global.encrypt);
			if(StringUtils.isNotBlank(encryptStr)){
				if("true".equalsIgnoreCase(encryptStr)){
					encrypt = true;
				}
			}
			this.intFileLimit(map);
			String target = map.get(Global.returnHandler);
			if(StringUtils.isNotBlank(target)){
				UploadReturnResultHandler  object = null;
				if(SpringContextHolder.containsBean(target)) {
			        object = SpringContextHolder.getBean(target);
			    }else {
			        try {
						object = (UploadReturnResultHandler) SpringContextHolder.getBean(Class.forName(target));
					} catch (ClassNotFoundException e) {
						e.printStackTrace();
					}
			    }
			    if(object == null) {
			    	logger.warn(target+"对象不存在！");
			    }else{
			    	logger.info("加载文件上传结果返回值"+target+"成功！");
			    	returnHander = object;
			    }
			}
			target = map.get(Global.downReturnHander);
			if(StringUtils.isNotBlank(target)){
				DownReturnResultHandler  object = null;
				if(SpringContextHolder.containsBean(target)) {
			        object = SpringContextHolder.getBean(target);
			    }else {
			        try {
						object = (DownReturnResultHandler) SpringContextHolder.getBean(Class.forName(target));
					} catch (ClassNotFoundException e) {
						e.printStackTrace();
					}
			    }
			    if(object == null) {
			    	logger.warn(target+"对象不存在！");
			    }else{
			    	logger.info("加载文件下载结果返回值"+target+"成功！");
			    	downReturnHander = object;
			    }
			}
			
		}
		this.fileLimit.init();
	}

	@Override
	public void afterPropertiesSet() throws Exception {

	}

	public FileLimit getFileLimit() {
		return this.fileLimit;
	}

	public void setFileLimit(FileLimit fileLimit) {
		this.fileLimit = fileLimit;
	}
}
